//---------------------------------------------------------------
//
//				Supper AB530x
//huajun @ 2019-05-30
/*LPWM 是硬件PWM模块产生的，不占用TIMER，推荐使用LPWM；
  TPWM 由TIMER产生，占用TIMER。
*/

#include "include.h"
#include "port_pwm_ex.h"

#if 0  //LPWM_EXAMPLE
//----------------------------------------------
//LPWM
static lpwm_handle_strc lpwm_note[LPWM_HANDLE_MAX];
static const u32 LPWM_MAP[3][5] = {
									{0x00, LPWM0MAP_PE4, LPWM1MAP_PE5, LPWM2MAP_PE6, LPWM3MAP_PE7},
									{0x00, LPWM0MAP_PB3, LPWM1MAP_PB4, LPWM2MAP_PE0, LPWM3MAP_PF2},
									{0x00, LPWM0MAP_PA0, LPWM1MAP_PA1, LPWM2MAP_PA2, LPWM3MAP_PA3}
								  };
static const u8 LPWM_MAP_PIN[3][5] = {
										{IO_NONE, IO_PE4, IO_PE5, IO_PE6, IO_PE7},
										{IO_NONE, IO_PB3, IO_PB4, IO_PE0, IO_PF2},
										{IO_NONE, IO_PA0, IO_PA1, IO_PA2, IO_PA3},
								  	 };

/*******************************************************************************
* lpwm_create
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Open lpwm and return handler
* INPUT: pin1-pin4 :Be in the same group.
* OUTPUT: NULL
* RETURN: Handler/FAIL.
*******************************************************************************/
s8 lpwm_create(lpwm_pin pin1, lpwm_pin pin2, lpwm_pin pin3, lpwm_pin pin4)
{
	u8 group = pin1 | pin2 | pin3 | pin4;
	s8 ret = -1;
	gpio_t lpwm_gpin;

	group = group & 0xF0;
	if((group != 0x10) && (group != 0x20) && (group != 0x40)){
		return -1;		//group err
	}
	ret = group >> 5;

	lpwm_note[ret].lpwm_con1 = LPWM_MAP[ret][pin1&0x0F] | LPWM_MAP[ret][pin2&0x0F] | \
							   LPWM_MAP[ret][pin3&0x0F] | LPWM_MAP[ret][pin4&0x0F];
	lpwm_note[ret].duty =  (pin1&0x0F);
	lpwm_note[ret].duty |= (pin2&0x0F) << 3;
	lpwm_note[ret].duty |= (pin3&0x0F) << 6;
	lpwm_note[ret].duty |= (pin4&0x0F) << 9;

	//Init GPIO
	for(u8 i=0; i<4; i++){
		bsp_gpio_cfg_init(&lpwm_gpin, LPWM_MAP_PIN[ret][lpwm_note[ret].duty>>(i*3) & 0x07]);
		if((LPWM_MAP_PIN[ret][lpwm_note[ret].duty>>(i*3) & 0x07] == IO_PB3) || (LPWM_MAP_PIN[ret][lpwm_note[ret].duty>>(i*3) & 0x07] == IO_PB4)){
			USBCON0 = 0;
			USBCON1 = 0;
		}
		lpwm_gpin.sfr[GPIOxDE]	|= BIT(lpwm_gpin.num);
		lpwm_gpin.sfr[GPIOxDIR] &= ~BIT(lpwm_gpin.num);
		lpwm_gpin.sfr[GPIOxFEN] |= BIT(lpwm_gpin.num);
	}

	return ret;
}


/*******************************************************************************
* lpwm_start
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Start lpwm and write duty vlaue
* INPUT: fd :   Values for results of lpwm_open
		period: Frequency = 26MHz/4/(period+1)  { 100Hz- 3250000Hz}
		duty1-duty4: Duty = Duty(n)/period
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_start(s8 fd, u16 period, u16 duty1, u16 duty2, u16 duty3, u16 duty4)
{
	u32 duty01 = 0x00, duty23 = 0x00;
	u16 duty[4], temp;
	u8  pwm_enable = 0;

	if(fd < 0){
		return -1;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	if(!lpwm_note[fd].duty){
		return -3;
	}

	duty[0] = duty1;
	duty[1] = duty2;
	duty[2] = duty3;
	duty[3] = duty4;
	temp = lpwm_note[fd].duty;

	for(u8 i=0; i<4; i++){
		switch(temp & 0x07){
			case 1:
				duty01 |= duty[i];
				pwm_enable |= BIT(0);
				break;
			case 2:
				duty01 |= (u32)duty[i] << 16;
				pwm_enable |= BIT(1);
				break;
			case 3:
				duty23 |= duty[i];
				pwm_enable |= BIT(2);
				break;
			case 4:
				duty23 |= (u32)duty[i] << 16;
				pwm_enable |= BIT(3);
				break;
		}

		temp >>= 3;
	}

	FUNCMCON1 = lpwm_note[fd].lpwm_con1;
	PWMPRCON  = period;
	PWM01DUTY = duty01;
	PWM23DUTY = duty23;

    if ((PWMCON & 0x0F) != pwm_enable){
        PWMCON |= pwm_enable;
    }

	return 0;
}

/*******************************************************************************
* lpwm_stop
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Stop lpwm
* INPUT: fd :   Values for results of lpwm_open
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_stop(s8 fd)
{
	gpio_t lpwm_gpin;

	if(fd < 0){
		return -1;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	if(!lpwm_note[fd].duty){
		return -3;
	}
	//Init GPIO
	for(u8 i=0; i<4; i++){
		bsp_gpio_cfg_init(&lpwm_gpin, LPWM_MAP_PIN[fd][lpwm_note[fd].duty>>(i*3) & 0x07]);
		lpwm_gpin.sfr[GPIOxDE]	|= BIT(lpwm_gpin.num);
		lpwm_gpin.sfr[GPIOxDIR] |= BIT(lpwm_gpin.num);
	}
	lpwm_note[fd].duty = 0;
	PWMCON = 0x00;

	return 0;
}


/*******************************************************************************
* lpwm_fast_period
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Fast set period, Call after lpwm_start
* INPUT: fd :   values for results of lpwm_open
		period: Frequency = 26MHz/4/(period+1)  { 100Hz- 3250000Hz}
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_fast_period(s8 fd, u16 period)
{
	if(fd < 0){
		return -1;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	if(!lpwm_note[fd].duty){
		return -3;
	}

	PWMPRCON  = period;

	return 0;
}


/*******************************************************************************
* lpwm_fast_duty
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Fast set duty, Call after lpwm_start
* INPUT: fd :   Values for results of lpwm_open
		duty1-duty4: duty = duty(n)/period
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_fast_duty(s8 fd, u16 duty1, u16 duty2, u16 duty3, u16 duty4)
{
	if(fd < 0){
		return -1;
	}

	if(!lpwm_note[fd].duty){
		return -3;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	PWM01DUTY = duty1| ((u32)duty2 << 16);
	PWM23DUTY = duty3| ((u32)duty4 << 16);
	return 0;
}

/*******************************************************************************
* lpwm_invert
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Set PWM invert
* INPUT: level :  0: Duty is high level, 1:Duty is low level.
* OUTPUT: NULL
* RETURN: Success or Fail.
*******************************************************************************/
s8 lpwm_invert(u8 level)
{
	if(level){
		PWMCON |= BIT(5);
	}else{
		PWMCON &= ~BIT(5);
	}

	return 0;
}


/*******************************************************************************
* lpwm_adjust_cycm
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Set cycm
* INPUT: fd :   Values for results of lpwm_open
		cycm1-4:set cycm for duty adjust step.
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_adjust_cycm(s8 fd, u8 cycm1, u8 cycm2, u8 cycm3, u8 cycm4)
{
	u32 cycm_reg = 0x00;
	u8 cycm[4], temp;

	if(fd < 0){
		return -1;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	if(!lpwm_note[fd].duty){
		return -3;
	}

	cycm[0] = cycm1;
	cycm[1] = cycm2;
	cycm[2] = cycm3;
	cycm[3] = cycm4;
	temp = lpwm_note[fd].duty;

	for(u8 i=0; i<4; i++){
		switch(temp & 0x07){
			case 1:
				cycm_reg |= cycm[i];
				break;
			case 2:
				cycm_reg |= (u32)cycm[i] << 8;
				break;
			case 3:
				cycm_reg |= (u32)cycm[i] << 16;
				break;
			case 4:
				cycm_reg |= (u32)cycm[i] << 24;
				break;
		}
		temp >>= 3;
	}
	PWMCYCNUM = cycm_reg;
	return 0;
}


/*******************************************************************************
* lpwm_adjust_step
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Set step and call the start function after him
* INPUT: fd :
		step1-4: Set steps for duty adjust step. Bit8 is add and subtract direction, 0 add, subtract.
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_adjust_step(s8 fd, u8 step1, u8 step2, u8 step3, u8 step4)
{
	u32 step_reg = 0x00;
	u8 step[4], temp;

	if(fd < 0){
		return -1;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	if(!lpwm_note[fd].duty){
		return -3;
	}

	step[0] = step1;
	step[1] = step2;
	step[2] = step3;
	step[3] = step4;
	temp = lpwm_note[fd].duty;

	for(u8 i=0; i<4; i++){
		switch(temp & 0x07){
			case 1:
				step_reg |= step[i];
				break;
			case 2:
				step_reg |= (u32)step[i] << 8;
				break;
			case 3:
				step_reg |= (u32)step[i] << 16;
				break;
			case 4:
				step_reg |= (u32)step[i] << 24;
				break;
		}
		temp >>= 3;
	}

	PWMSTEP = step_reg;

	return 0;
}


/*******************************************************************************
* lpwm_adjust_start
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Start adjusts
* INPUT: fd :
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_adjust_start(s8 fd)
{
	if(fd < 0){
		return -1;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	if(!lpwm_note[fd].duty){
		return -3;
	}

	PWMCON |= BIT(4);

	return 0;
}


/*******************************************************************************
* lpwm_adjust_stop
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Stop adjusts
* INPUT: fd :
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 lpwm_adjust_stop(s8 fd)
{
	if(fd < 0){
		return -1;
	}

	if(fd >= LPWM_HANDLE_MAX){
		return -2;
	}

	PWMCON &= ~BIT(4);

	return 0;
}



//------------------------------------------------------------------------------
//	Timer use for PWM
/* timer 使用情况
timer0 : 系统时钟使用
timer1:  (可选)  ledseg_ajust.  7脚屏的时候使用(亮度均匀调节).  timer1_irq_init 
timer2:  (可选)    注意timer2和timer4/5用同一个中断向量入口, 需要timer2及tws时的话, 在tws_timer_isr查timer2的pending.  不要重新注册timer2, 会覆盖掉以前的timer4/5中断向量. 
timer3:  (可选)    软件红外解码使用...
timer4/timer5 : tws使用用了.  

timer3/timer4/timer5 只有可以产生PWM
*/


//----------------------------------------------
//TPWM
static tpwm_handle_strc tpwm_note[TPWM_HANDLE_MAX];
static const u8 TPWM_MAP_PIN[3][4] = {
										{IO_NONE, IO_PB0, IO_PB1, IO_PB2},
										{IO_NONE, IO_PA5, IO_PA6, IO_PA7},
										{IO_NONE, IO_PE1, IO_PE2, IO_PE3},
								  	 };
static const tpwm_config_strc tpwm_config[3] = {
	{
		.TMRxCON 	= &TMR3CON,
		.TMRxCNT 	= &TMR3CNT,
		.TMRxPR		= &TMR3PR,
		.TMRxDUTY0	= &TMR3DUTY0,
		.TMRxDUTY1  = &TMR3DUTY1,
		.TMRxDUTY2  = &TMR3DUTY2,
	},

	{
		.TMRxCON	= &TMR4CON,
		.TMRxCNT	= &TMR4CNT,
		.TMRxPR 	= &TMR4PR,
		.TMRxDUTY0	= &TMR4DUTY0,
		.TMRxDUTY1	= &TMR4DUTY1,
		.TMRxDUTY2	= &TMR4DUTY2,
	},

	{
		.TMRxCON 	= &TMR5CON,
		.TMRxCNT 	= &TMR5CNT,
		.TMRxPR		= &TMR5PR,
		.TMRxDUTY0	= &TMR5DUTY0,
		.TMRxDUTY1  = &TMR5DUTY1,
		.TMRxDUTY2  = &TMR5DUTY2,
	},
};

/*******************************************************************************
* tpwm_create
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Create timer PWM
* INPUT: pin1-pin3 : timer pwm pin
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 tpwm_create(tpwm_pin pin1, tpwm_pin pin2, tpwm_pin pin3)
{
	u8 group = pin1 | pin2 | pin3;
	s8 ret = -1;
	gpio_t tpwm_gpin;

	printf("group: %x\r\n", group);
	group = group & 0xF0;
	if((group != 0x10) && (group != 0x20) && (group != 0x40)){
		return -1;		//group err, Must be in the same group
	}

	ret = group >> 5;

#if IRRX_SW_EN
	if(ret == 0){
		return -2;
	}
#endif

#if BT_TWS_EN
	if(ret){
		return -3;
	}
#endif
	tpwm_note[ret].pin_max =  (pin1&0x0F);
	tpwm_note[ret].pin_max |= (pin2&0x0F) << 3;
	tpwm_note[ret].pin_max |= (pin3&0x0F) << 6;

	//Init GPIO
	for(u8 i=0; i<3; i++){
		bsp_gpio_cfg_init(&tpwm_gpin, TPWM_MAP_PIN[ret][tpwm_note[ret].pin_max>>(i*3) & 0x07]);
		tpwm_gpin.sfr[GPIOxDE]	|= BIT(tpwm_gpin.num);
		tpwm_gpin.sfr[GPIOxDIR] &= ~BIT(tpwm_gpin.num);
		tpwm_gpin.sfr[GPIOxFEN] |= BIT(tpwm_gpin.num);
	}

	//func map
	FUNCMCON2 &= ~(0x0F << (8 + 4*ret));
	FUNCMCON2 |= (0x01 << (8 + 4*ret));

	//config
	*(tpwm_config[ret].TMRxCON) = 0;
	*(tpwm_config[ret].TMRxCNT) = 0;

	return ret;
}


/*******************************************************************************
* tpwm_start
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: start pwm
* INPUT: fd: Open tpwm and return handler
		 period: Frequency = sysclk/(period+1)
		 duty1-duty3: duty = duty_x/period;  (0 - period)
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 tpwm_start(s8 fd, u16 period, u16 duty1, u16 duty2, u16 duty3)
{
	u16 duty[4], temp;

	if(fd < 0){
		return -1;
	}

	if(fd >= TPWM_HANDLE_MAX){
		return -2;
	}

	if(!tpwm_note[fd].pin_max){
		return -3;
	}

	duty[0] = duty1;
	duty[1] = duty2;
	duty[2] = duty3;
	temp = tpwm_note[fd].pin_max;

	for(u8 i=0; i<3; i++){
		switch(temp & 0x07){
			case 1:
				*(tpwm_config[fd].TMRxDUTY0) = duty[i];
				*(tpwm_config[fd].TMRxCON) |= BIT(9);
				break;
			case 2:
				*(tpwm_config[fd].TMRxDUTY1) = duty[i];
				*(tpwm_config[fd].TMRxCON) |= BIT(10);
				break;
			case 3:
				*(tpwm_config[fd].TMRxDUTY2) = duty[i];
				*(tpwm_config[fd].TMRxCON) |= BIT(11);
				break;
		}
		temp >>= 3;
	}

	*(tpwm_config[fd].TMRxPR)  = period;		//period
	*(tpwm_config[fd].TMRxCON) |= BIT(0);		//start
	return 0;
}


/*******************************************************************************
* tpwm_stop
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: stop pwm
* INPUT: fd: Open tpwm and return handler
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 tpwm_stop(s8 fd)
{
	if(fd < 0){
		return -1;
	}

	if(fd >= TPWM_HANDLE_MAX){
		return -2;
	}

	if(!tpwm_note[fd].pin_max){
		return -3;
	}

	tpwm_note[fd].pin_max = 0;
	*(tpwm_config[fd].TMRxCON) &= ~(BIT(9)|BIT(10)|BIT(11));
	*(tpwm_config[fd].TMRxCON) &= ~BIT(0);		//start

	return 0;
}


/*******************************************************************************
* tpwm_fast_period
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION:
* INPUT: fd: Open tpwm and return handler
		period: Frequency = sysclk/(period+1)
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 tpwm_fast_period(s8 fd, u16 period)
{

	if(fd < 0){
		return -1;
	}

	if(fd >= TPWM_HANDLE_MAX){
		return -2;
	}

	if(!tpwm_note[fd].pin_max){
		return -3;
	}

	*(tpwm_config[fd].TMRxPR)  = period;		//period

	return 0;
}

/*******************************************************************************
* lpwm_fast_duty
* Author/Date: Huajun, 30/05/2019
* DESCRIPTION: Fast set duty, Call after lpwm_start
* INPUT: fd :   Values for results of lpwm_open
		duty1-duty4: duty = duty(n)/period
* OUTPUT: NULL
* RETURN: Success or fail.
*******************************************************************************/
s8 tpwm_fast_duty(s8 fd, u16 duty1, u16 duty2, u16 duty3)
{
	if(fd < 0){
		return -1;
	}

	if(fd >= TPWM_HANDLE_MAX){
		return -2;
	}

	if(!tpwm_note[fd].pin_max){
		return -3;
	}

	*(tpwm_config[fd].TMRxDUTY0) = duty1;
	*(tpwm_config[fd].TMRxDUTY1) = duty2;
	*(tpwm_config[fd].TMRxDUTY2) = duty3;

	return 0;
}

#endif  //LPWM_EXAMPLE
